package com.ruoyi.system.controller;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.JwtUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.TableDataInfo;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import com.ruoyi.system.utils.AllTickUtils;
import com.ruoyi.system.utils.LuaUtils;
import com.ruoyi.system.utils.TCPClient;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.ruoyi.common.core.utils.PageUtils.startPage;
import static com.ruoyi.common.security.utils.SecurityUtils.getLoginUser;


@RestController
@RequestMapping("/stock")
public class SharePriceController {

    @Autowired
    private SherePriceService sherePriceService;
    @Autowired
    private SharePlateService sharePlateService;
    @Autowired
    private ShareCertificateService shareCertificateService;
    @Autowired
    private SysContractsService contractsService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    TokenService tokenService;
    @Autowired
    ISysUserService Userservice;
    @Autowired
    private ShareKService shareKService;
    @Autowired
    private SysUserScokService sysUserScokService;

    private static final String LOCK_KEY_BUY = "getBuyLock:";
    private static final String LOCK_KEY_SELL = "getSellLock:";

    /**
     * 查询股票列表增删改查列表
     */
    @RequiresPermissions("system:stock:list")
    @RequestMapping("/list")
    public R list(){
        startPage();
        List<SherePrice> list = sherePriceService.list();
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setRows(list);
        rspData.setMsg("查询成功");
        rspData.setTotal(new PageInfo(list).getTotal());
        return R.ok(rspData);
    }

    public static final String token = "f742f23c2ad0271d16d370eb6168f205-c-app";
    //最新股票价格
    @RequestMapping("/sharePriceList/{code}")
    public R sharePriceList(@PathVariable("code") String code) throws IOException {
        ShareCertificate shareCertificate =  shareCertificateService.getShareName(code);
        ShareK allTickData = AllTickUtils.getAllTickData(code,shareCertificate.getShareName());
        if (allTickData!=null){
            shareKService.addShareK(allTickData);
        }
        //如果没有数据走MySql数据库获取最新数据
        ShareK shareKs = shareKService.getStock(code);
        return R.ok(shareKs.getPrice());
    }

    /**
     * 股票下拉
     * @param id
     * @return
     */
    @RequestMapping("/getShareCertificateList/{id}")
    public R getShareCertificateList(@PathVariable("id") Integer id){
        List<ShareCertificate> list = shareCertificateService.getShareCertificateList(id);
        return R.ok(list);
    }
    /**
     * 板块列表
     * @return
     */
    @RequestMapping("/sharePlate")
    public R sharePlateList(){
        List<SharePlate> list = sharePlateService.sharePlateList();
        return R.ok(list);
    }

    /**
     * 股票数据获取
     * @throws Exception
     */
    @RequestMapping("/addSharePrice")
    public void addSharePrice() throws Exception {
        //获取股票码
        List<ShareCertificate> shareCertificateList = shareCertificateService.getCode();
        for (ShareCertificate s : shareCertificateList) {
            String allTickKData = AllTickUtils.getAllTickKData(s.getCode());
            if (allTickKData!=null){
                //解析数据
                JsonObject jsonObject = new JsonObject(allTickKData);
                System.out.println(jsonObject);
                JsonObject dataObject = jsonObject.getJsonObject("data");
                JsonArray tickListArray = dataObject.getJsonArray("kline_list");
                JsonObject jsonObject1 = tickListArray.getJsonObject(0);

                String timestamp = jsonObject1.getString("timestamp");
                String openPrice = jsonObject1.getString("open_price");
                String closePrice = jsonObject1.getString("close_price");
                String highPrice = jsonObject1.getString("high_price");
                String lowPrice = jsonObject1.getString("low_price");
                String volume = jsonObject1.getString("volume");
                String turnover = jsonObject1.getString("turnover");

                SherePrice sherePrice = new SherePrice();
                sherePrice.setTimestamp(timestamp);
                sherePrice.setOpenPrice(new BigDecimal(openPrice));
                sherePrice.setClosePrice(new BigDecimal(closePrice));
                sherePrice.setHigePrice(new BigDecimal(highPrice));
                sherePrice.setLowPrice(new BigDecimal(lowPrice));
                sherePrice.setVolume(volume);
                sherePrice.setTurnover(turnover);
                sherePrice.setShereName(s.getShareName());
                sherePriceService.addSharePrice(sherePrice);
            }
        }
    }

    /**
     * 买入
     * @param num
     * @param scokname
     * @param price
     * @param scokId
     * @return
     */
    @RequestMapping("/buy/{num}/{scokname}/{price}/{scokId}")
    public AjaxResult buy(  @PathVariable("num") Integer num
            , @PathVariable("scokname") String scokname,@PathVariable("price") BigDecimal price,
                          @PathVariable("scokId") String scokId
    ) throws Exception {
        SysUserScok sysUserScok = new SysUserScok();
        sysUserScok.setNum(num);
        sysUserScok.setScokName(scokname);
        sysUserScok.setScokPrice(price);
        sysUserScok.setScokId(scokId);
        String ipAddr = IpUtils.getIpAddr();
        String s = DigestUtil.md5Hex(sysUserScok.toString());
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(s + "buy:ip" + ipAddr, "11", 1, TimeUnit.MINUTES);
        if (!aBoolean){
            return AjaxResult.error("禁止重复提交");
        }
        LoginUser loginUser = getLoginUser();
        Long userId = loginUser.getSysUser().getUserId();
        BigDecimal money = Userservice.selectUserById(userId).getMoney();
        if (money.compareTo(price.multiply(new BigDecimal(num))) == -1){
            return AjaxResult.error("余额不足");
        }
        //加入委托
        boolean add = contractsService.add(num, scokname, price, scokId,0);
        return add?AjaxResult.success():AjaxResult.error("请勿重复提交");
    }


    /**
     * 卖出
     * @param num
     * @param scokname
     * @param price
     * @param scokId
     * @return
     */
    @RequestMapping("/sell/{num}/{scokname}/{price}/{scokId}")
    public AjaxResult sell(  @PathVariable("num") Integer num
            , @PathVariable("scokname") String scokname,@PathVariable("price") BigDecimal price,
                            @PathVariable("scokId") String scokId
    ) throws Exception {
        SysUserScok sysUserScok = new SysUserScok();
        sysUserScok.setNum(num);
        sysUserScok.setScokName(scokname);
        sysUserScok.setScokPrice(price);
        sysUserScok.setScokId(scokId);
        String ipAddr = IpUtils.getIpAddr();
        String s = DigestUtil.md5Hex(sysUserScok.toString());
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(s + "sell:ip" + ipAddr, "11", 1, TimeUnit.MINUTES);
        if (!aBoolean){
            return AjaxResult.error("禁止重复提交");
        }
        LoginUser loginUser = getLoginUser();
        Long userId = loginUser.getSysUser().getUserId();
//        BigDecimal money = Userservice.selectUserById(userId)();
        SysUserScok selectbyid = contractsService.selectByuidandcid(userId, scokId);
        if (ObjectUtil.isEmpty(selectbyid)){
            return AjaxResult.error("你没有该股票请重新输入");
        }
        if (selectbyid.getNum()<num){
            return AjaxResult.error("你的数量不够");
        }
        //加入委托
        boolean add = contractsService.add(num, scokname, price, scokId,1);
        return add?AjaxResult.success():AjaxResult.error();
    }

    /**
     * 股票下拉列表
     * @return
     */
    @RequestMapping("/buyStockName")
    public R buyStockName(){
       List<ShareCertificate> list = shareCertificateService.getList();
       return R.ok(list);
    }

    /**
     * 买入委托
     * @param sysContracts
     * @param request
     * @return
     */
    @RequestMapping("/getBuy")
    public R getBuy(@RequestBody SysContracts sysContracts, HttpServletRequest request) {
        String lockKey = LOCK_KEY_BUY + IpUtils.getIpAddr(request);
        try {
            // 尝试获取分布式锁，超时时间设为 10 秒
            if (acquireDistributedLock(lockKey, 10000)) {
                try {
                    // 检查 Redis 中指定键是否存在
                    if (!redisTemplate.hasKey("buy:ip" + IpUtils.getIpAddr(request))) {
                        String token = SecurityUtils.getToken(request);
                        String userName = JwtUtils.getUserName(token);
                        String userId1 = JwtUtils.getUserId(token);
                        sysContracts.setUserId(Integer.valueOf(userId1));
                        sysContracts.setUname(userName);
                        sysContracts.setStatus(0);
                        sysContracts.setIndate(new Date());
                        sysContracts.setFalg(0);

                        // 金额判断
                        LoginUser loginUser = getLoginUser();
                        Long userId = loginUser.getSysUser().getUserId();
                        BigDecimal money = Userservice.selectUserById(userId).getMoney();
                        if (money.compareTo(sysContracts.getScokPrice().multiply(new BigDecimal(sysContracts.getNum()))) == -1) {
                            return R.fail("余额不足");
                        }
                        //预扣金额:先获取卖出总金额，再减去
                        BigDecimal multiply = sysContracts.getScokPrice().multiply(new BigDecimal(sysContracts.getNum()));
                        money = money.subtract(multiply);
                        Userservice.updateMoney(money, Math.toIntExact(userId));
                        // 添加数据
                        contractsService.addContracts(sysContracts);
                        TCPClient.tcpClient(sysContracts);
                        redisTemplate.opsForValue().set("buy:ip" + IpUtils.getIpAddr(request), "11", 1, TimeUnit.MINUTES);
                        return R.ok("委托申请成功", "200");
                    } else {
                        return R.fail("请勿重复提交");
                    }
                } finally {
                    // 释放分布式锁
                    releaseDistributedLock(lockKey);
                }
            } else {
                return R.fail("操作繁忙，请稍后重试");
            }
        } catch (Exception e) {
            // 捕获并处理可能出现的异常
            return R.fail("系统异常，请稍后重试");
        }
    }

    /**
     * 卖出委托
     * @param sysContracts
     * @param request
     * @return
     */
    @RequestMapping("/getSell")
    public R getSell(@RequestBody SysContracts sysContracts, HttpServletRequest request) {
        String lockKey = LOCK_KEY_SELL + sysContracts.toString();
        try {
            // 尝试获取分布式锁，超时时间设为 10 秒
            if (acquireDistributedLock(lockKey, 10000)) {
                try {
                    // 检查 Redis 中指定键是否存在
                    if (!redisTemplate.hasKey(sysContracts.toString())) {
                        // 判断持股数量
                        SysUserScok userScok = sysUserScokService.getCodeNum(sysContracts.getStockId());
                        System.err.println(userScok);
                        if (userScok.getNum() < sysContracts.getNum()) {
                            return R.fail("你没有那么多股票");
                        } else {
                            // 修改数据
                            userScok.setNum(userScok.getNum() - sysContracts.getNum());
                            //contractsService.updateSysContracts(sysContracts);
                            sysUserScokService.updateByScok(userScok);
                            // 添加数据
                            String token = SecurityUtils.getToken(request);
                            String userName = JwtUtils.getUserName(token);
                            String userId1 = JwtUtils.getUserId(token);
                            sysContracts.setUserId(Integer.valueOf(userId1));
                            sysContracts.setUname(userName);
                            sysContracts.setStatus(0); // 委托状态
                            sysContracts.setIndate(new Date()); // 委托时间
                            sysContracts.setFalg(1); // 卖出
                            contractsService.addContracts(sysContracts);//添加卖出委托
                            TCPClient.tcpClient(sysContracts);
                            redisTemplate.opsForValue().set(sysContracts.toString(), "11", 1, TimeUnit.MINUTES);
                            return R.ok("委托申请成功", "200");
                        }
                    } else {
                        return R.fail("请勿重复提交");
                    }
                } finally {
                    // 释放分布式锁
                    releaseDistributedLock(lockKey);
                }
            } else {
                return R.fail("操作繁忙，请稍后重试");
            }
        } catch (Exception e) {
            // 捕获并处理可能出现的异常
            return R.fail("系统异常，请稍后重试");
        }
    }
    @RequestMapping("/myEntrust")
    public R myEntrust(HttpServletRequest request){
        String token = SecurityUtils.getToken(request);
        String userId1 = JwtUtils.getUserId(token);
       List<SysContracts> sysContractsList = contractsService.myEntrust(Integer.parseInt(userId1));
       return R.ok(sysContractsList);
    }

    // 获取分布式锁
    private boolean acquireDistributedLock(String lockKey, long timeoutMillis) {
        long currentTime = System.currentTimeMillis();
        long expirationTime = currentTime + timeoutMillis + 1;
        String expirationValue = String.valueOf(expirationTime);

        // 使用 setnx 尝试获取锁
        if (redisTemplate.opsForValue().setIfAbsent(lockKey, expirationValue)) {
            return true;
        }

        // 检查锁是否过期
        String existingValue = (String) redisTemplate.opsForValue().get(lockKey);
        if (existingValue!= null && Long.parseLong(existingValue) < currentTime) {
            // 锁已过期，获取并更新锁
            String oldValue = (String) redisTemplate.opsForValue().getAndSet(lockKey, expirationValue);
            if (oldValue!= null && oldValue.equals(existingValue)) {
                return true;
            }
        }

        return false;
    }

    // 释放分布式锁
    private void releaseDistributedLock(String lockKey) {
        String value = (String) redisTemplate.opsForValue().get(lockKey);
        if (value!= null && Long.parseLong(value) >= System.currentTimeMillis()) {
            redisTemplate.delete(lockKey);
        }
    }





    /**
     * 秒杀
     * @return
     */
    @RequestMapping("/secKill")
    public R secKill(Integer userId) throws IOException {
        //获取用户ID
        String LOCK_KEY_SECKILL = "seckill" + userId;
        //分布式锁
        Boolean lockAcquired = acquireDistributedLock(LOCK_KEY_SECKILL, 10000);
        if (lockAcquired != null && lockAcquired) {

            //调用方法
            String s = LuaUtils.LuaCall( userId);
            return R.ok(s);
        }
        return R.fail("异常错误");
    }
}
